package com.ihome.framework.core.sftp;

import com.ihome.framework.core.exception.BusinessException;
import com.ihome.framework.core.log.Log;
import com.ihome.framework.core.utils.JsonUtil;
import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;

public class SFtpChannelFactory implements IChannelFactory {

	private Session session;

	private AtomicInteger size = new AtomicInteger(0);

	private SFtpConfig sftpConfig;

	public SFtpConfig getSftpConfig() {
		return sftpConfig;
	}

	public void setSftpConfig(SFtpConfig sftpConfig) {
		this.sftpConfig = sftpConfig;
	}

	private static final int MAX_SIZE = 50;
	private static final Logger LOGGER = LoggerFactory.getLogger(SFtpChannelFactory.class);

	public void init() {
		/*
		 * size.set(0); LOGGER.info(Log.op("SftpChannelFactory.init").
		 * msg("init the sftp start.").kv("key",
		 * sftpConfig.getPrivatekey()).kv("user",
		 * sftpConfig.getUsername()).toString()); try { synchronized (this) {
		 * session = getNewSession(true); }
		 * LOGGER.info(Log.op("SftpChannelFactory.init").
		 * msg("init the sftp successfully.").kv("key",
		 * sftpConfig.getPrivatekey()).kv("user",
		 * sftpConfig.getUsername()).toString()); } catch (JSchException e) {
		 * LOGGER.error(Log.op("SftpChannelFactory.init").
		 * msg("init the sftp failure.").kv("key",
		 * sftpConfig.getPrivatekey()).kv("user",
		 * sftpConfig.getUsername()).toString(), e); //throw new
		 * BusinessException("init sftp failure", e); }
		 */
	}

	/*
	 * @Override public Channel openChannel() { if (size.get() > MAX_SIZE) {
	 * LOGGER.error(Log.op("SftpChannelFactory.openChannel").
	 * msg("the channel has arrive at the max number.").kv("size",
	 * size.get()).toString()); throw new
	 * BusinessException("the channel has arrive at the max number."); }
	 * LOGGER.info(Log.op("SftpChannelFactory.openChannel").
	 * msg("open the channel starts.").kv("key",
	 * session.isConnected()).toString()); try { int count =
	 * size.incrementAndGet(); synchronized (this) { ChannelSftp channelSftp =
	 * openChannel(session);
	 * LOGGER.info(Log.op("SftpChannelFactory.openChannel").
	 * msg("open the channel successfully.").kv("key",
	 * session.isConnected()).kv("size", count) .toString()); return
	 * channelSftp; } } catch (JSchException e) {
	 * LOGGER.error(Log.op("SftpChannelFactory.openChannel").
	 * msg("open the channel failure.").toString(), e); destory(); init(); throw
	 * new BusinessException("open the channel failure", e); } }
	 */

	private Session getNewSession(boolean connectNow) throws JSchException {
		JSch jsch = getJSch(sftpConfig);
		Session newSession = jsch.getSession(sftpConfig.getUsername(), sftpConfig.getHost(), sftpConfig.getPort());
		newSession.setPassword(sftpConfig.getPassword());
		Properties config = new Properties();
		config.put("StrictHostKeyChecking", "no");
		newSession.setConfig(config);
		newSession.setTimeout(sftpConfig.getTimeout());
		if (connectNow) {
			newSession.connect();
		}
		return newSession;
	}

	private JSch getJSch(SFtpConfig sftpConfig) throws JSchException {
		JSch jsch = new JSch();
		if (sftpConfig.getPrivatekeyValue() != null && !"".equals(sftpConfig.getPrivatekeyValue())) {
			byte[] prvkey = sftpConfig.getPrivatekeyValue().getBytes(Charset.defaultCharset());
			byte[] passphrase = sftpConfig.getPrivatekeypassphrase() == null ? null
					: sftpConfig.getPrivatekeypassphrase().getBytes(Charset.defaultCharset());
			jsch.addIdentity(sftpConfig.getUsername(), prvkey, null, passphrase);
		} else if (sftpConfig.getPrivatekey() != null && !"".equals(sftpConfig.getPrivatekey())) {
			jsch.addIdentity(sftpConfig.getPrivatekey(), sftpConfig.getPrivatekeypassphrase());
		}
		return jsch;
	}

	private ChannelSftp openChannel(Session mySession) throws JSchException {
		ChannelSftp channelSftp;

		if (!mySession.isConnected()) {
			mySession.connect();
		}
		channelSftp = (ChannelSftp) mySession.openChannel("sftp");
		channelSftp.connect();
		return channelSftp;
	}

	@Override
	public Channel openChannelWithNewSession() {
		LOGGER.info(Log.op("SftpChannelFactory.openChannelWithNewSession").msg("open the channel starts.").toString());
		Session newSession = null;
		try {
			int count = size.incrementAndGet();
			newSession = getNewSession(true);
			ChannelSftp channelSftp = openChannel(newSession);
			LOGGER.info(Log.op("SftpChannelFactory.openChannelWithNewSession").msg("open the channel successfully.")
					.kv("key", newSession.isConnected()).kv("size", count).toString());
			return channelSftp;
		} catch (JSchException e) {
			LOGGER.error(
					Log.op("SftpChannelFactory.openChannelWithNewSession").msg("open the channel failure.").toString(),
					e);
			if (newSession != null && newSession.isConnected()) {
				newSession.disconnect();
			}
			throw new BusinessException("open the channel failure", e);
		}
	}

	@Override
	public Channel openChannelWithNewSession(SFtpConfig sftpConfig) {
		LOGGER.info(Log.op("SftpChannelFactory.openChannelWithNewSession(SFtpConfig)").msg("open the channel starts.")
				.kv("sftpConfig", JsonUtil.toJSONString(sftpConfig, Arrays.asList("privatekeyValue"))).toString());
		Session newSession = null;
		try {
			int count = size.incrementAndGet();
			JSch jsch = getJSch(sftpConfig);
			newSession = jsch.getSession(sftpConfig.getUsername(), sftpConfig.getHost(), sftpConfig.getPort());
			newSession.setPassword(sftpConfig.getPassword());
			Properties config = new Properties();
			config.put("StrictHostKeyChecking", "no");
			newSession.setConfig(config);
			newSession.setTimeout(sftpConfig.getTimeout());
			newSession.connect();
			ChannelSftp channelSftp = openChannel(newSession);
			LOGGER.info(Log.op("SftpChannelFactory.openChannelWithNewSession(SFtpConfig)")
					.msg("open the channel successfully.").kv("key", newSession.isConnected()).kv("size", count)
					.toString());
			return channelSftp;
		} catch (JSchException e) {
			LOGGER.error(Log.op("SftpChannelFactory.openChannelWithNewSession(SFtpConfig)")
					.msg("open the channel failure.").toString(), e);
			if (newSession != null && newSession.isConnected()) {
				newSession.disconnect();
			}
			throw new BusinessException("open the channel failure", e);
		}
	}

	/*
	 * @Override public void close(Channel channel) { int count =
	 * size.decrementAndGet();
	 * LOGGER.info(Log.op("SftpChannelFactory.close").msg("close the channel.").
	 * kv("channel id", channel.getId()).kv("size", count).toString()); if
	 * (channel != null && channel.isConnected()) { channel.disconnect(); } }
	 */

	@Override
	public void closeChannelAndSession(Channel channel) {
		int count = size.decrementAndGet();
		LOGGER.info(Log.op("SftpChannelFactory.close").msg("close the channel.").kv("channel id", channel.getId())
				.kv("size", count).toString());
		if (channel.isConnected()) {
			try {
				Session session = channel.getSession();
				if (session != null && session.isConnected()) {
					session.disconnect();
				}
			} catch (JSchException e) {
				LOGGER.error(Log.op("SftpChannelFactory.closeChannelAndSession(Channel)").msg("close session failure.")
						.toString(), e);
			}
			channel.disconnect();
		}
	}

	public void destory() {
		synchronized (this) {
			if (session != null && session.isConnected()) {
				session.disconnect();
			}
		}
	}

}
